package org.tri.form;

import java.awt.Point;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.tri.entity.AbstractAnimal;
import org.tri.entity.Transaccion;

/**
 * La clase {@code RegistroMovimientosExistencias} representa un documento que
 * tiene constancia de los movimientos y existencias de animales de un 
 * establecimiento o productor. *
 * @author kiira
 * @version 1.0, Jan 9, 2012
 */
public class RegistroMovimientosExistencias extends Form {

    /** Nombre del template a utilizar para esta form. */
    private static final String TEMPLATE = "mov_exi";
    /** Cantidad máxima de entradas por hoja del formulario. */
    private static final int CANT_MAX_PER_SHEET = 30;
    /** Cantidad máxima de caravanas por linea de observación. */
    private static final int CANT_MAX_CAR_OBS = 4;
    /** Primer celda donde se comienza a colocar datos. */
    private static final Point PRIMER_CELDA = new Point( 0, 3 );
    
    /** Set de transacciones ordenadas por fecha. */
    private SortedSet<Transaccion> transacciones;
    
    /**
     * Inicializa el registro, cargando los datos de las transacciones en
     * el template.
     * @param transacciones transacciones a representar por este registro.
     * @param output nombre que se desea apendar al archivo de salida producido
     * por este objeto al finalizar la carga de datos.
     * @throws FileNotFoundException si no se encuentra el template.
     * @throws IOException si no se puede abrir el template.
     * @throws InvalidFormatException si el template esta corrupto.
     */
    public RegistroMovimientosExistencias(
            List<Transaccion> transacciones, String output )
            throws FileNotFoundException, IOException, InvalidFormatException {
        super( TEMPLATE, output );

        //ponemos las transacciones en un sorted set para asegurarnos de que 
        //esten ordenadas por fecha
        this.transacciones = new TreeSet<Transaccion>(
        new ComparadorTransaccionesPorFecha() );
        
        this.transacciones.addAll( transacciones );

        //cargamos los datos en el formulario
        cargarDatos();

    }//end constructor

    /**
     * Carga los datos de las transacciones en el template.
     */
    private void cargarDatos() {
        //punto que indica la celda actual
        Point celdaActual = new Point( PRIMER_CELDA );

        //formatter para las fechas
        SimpleDateFormat format = new SimpleDateFormat( "dd/MM/yy" );

        //recorremos las transacciones
        for ( Transaccion transaccion : transacciones ) {
            //cargamos la fecha
            cargarYAvanzar( celdaActual, format.format(
                    transaccion.obtenerFecha() ) );

            //cargamos el tipo de movimiento
            cargarYAvanzar( celdaActual, transaccion.obtenerTipoMovimiento() );

            //cargamos el comprobante
            cargarYAvanzar( celdaActual, String.valueOf(
                    transaccion.obtenerNroComprobante() ) );

            //cargamos la renspa
            cargarYAvanzar( celdaActual, String.valueOf(
                    transaccion.obtenerEstablecimiento().getRenspa() ) );

            //TODO: cargar TRI si o no
            celdaActual.x++;

            //cargamos el movimiento
            if ( !transaccion.esEntrada() )
                celdaActual.x++;

            cargarYAvanzar( celdaActual, String.valueOf(
                    transaccion.obtenerAnimales().size() ) );

            if ( transaccion.esEntrada() )
                celdaActual.x++;

            //TODO: stock?? animales sin caravana??
            celdaActual.x += 2;

            //cargamos las caravanas
            cargarCaravanas( celdaActual, new ArrayList<AbstractAnimal>(
                    transaccion.obtenerAnimales() ) );

            //avanzamos a la siguiente fila
            celdaActual.x = PRIMER_CELDA.x;
            celdaActual.y++;

            //si superamos la cantidad disponible de lugares, pasamos a 
            //la siguiente hoja
            if ( celdaActual.y > PRIMER_CELDA.y + CANT_MAX_PER_SHEET ) {
                nextSheet();

                celdaActual.y = PRIMER_CELDA.y;

            }//end if

        }//end if

    }//end método cargarDatos

    /**
     * Carga un valor en la celda correspondiente y desplaza la misma un lugar
     * hacia la derecha para continuar cargando datos a continuación.
     * @param celda celda donde se desea cargar el dato.
     * @param text texto cargar.
     */
    private void cargarYAvanzar( Point celda, String text ) {
        appendText( celda, text );
        celda.translate( 1, 0 );

    }//end método cargarYAvanzar

    /**
     * Carga las caravanas en el formato desde-hasta, agregando las caravanas
     * que no correspondan al intervalo en la siguiente celda a la derecha.
     */
    private void cargarCaravanas( Point celdaActual,
                                  List<AbstractAnimal> animales ) {
        //ordenamos las caravanas por caravana
        Collections.sort( animales, new AbstractAnimalComparator() );

        //buscamos el intervalo mas largo de la lista
        AbstractAnimal ultimo = null;
        List<AbstractAnimal> intervaloMayor = null;
        List<AbstractAnimal> intervaloActual = new LinkedList<AbstractAnimal>();

        for ( AbstractAnimal animal : animales ) {
            if ( ultimo == null
                 || ultimo.esSiguienteCaravana( animal.getCaravana() ) ) {
                intervaloActual.add( animal );

                ultimo = animal;

            } else if ( intervaloMayor == null
                        || intervaloActual.size() > intervaloMayor.size() ) {
                intervaloMayor = intervaloActual;

                intervaloActual = new LinkedList<AbstractAnimal>();

            } else
                intervaloActual = new LinkedList<AbstractAnimal>();

        }

        //un checkeo final por si termino el ciclo sin permitir comparación
        if ( intervaloMayor == null || 
             intervaloActual.size() > intervaloMayor.size() )
            intervaloMayor = intervaloActual;

        //removemos los elementos del mayor de la lista general, y cargamos el
        // desde y el hasta SOLO si el intervalo es mayor a 1
        if ( intervaloMayor.size() > 1 ) {
            animales.removeAll( intervaloMayor );

            //cargamos el desde y el hasta
            cargarYAvanzar( celdaActual, intervaloMayor.get( 0 ).getCaravana() );
            cargarYAvanzar( celdaActual, intervaloMayor.get(
                    intervaloMayor.size() - 1 ).getCaravana() );

        } else
            celdaActual.x += 2;        
               
        //cargamos los restantes en las observaciones
        //antes de cargar tenemos que aumentar el tamaño de la fila si hiciera
        //falta, ya que si se ponen mas de 4 caravanas no se ven correctamente
        int multiplicador = animales.size() / CANT_MAX_CAR_OBS;
        
        aumentarAlturaFila( celdaActual.y, multiplicador );
        
        Iterator<AbstractAnimal> iter = animales.iterator();
        AbstractAnimal animal;
        
        boolean next = iter.hasNext();
        
        while( next ) {
            animal = iter.next();
            appendText( celdaActual, animal.getCaravana() );
            
            if ( !(next = iter.hasNext()) )
                break;
            
            appendText( celdaActual, ", " );
            
        }//end while

    }//end método cargarCaravanas

    /**
     * La clase {@code ComparadorTransaccionesPorFecha} compara dos 
     * transacciones, indicando cual es "menor" y cual es "mayor" de acuerdo
     * a la fecha de la transacción.
     */
    class ComparadorTransaccionesPorFecha implements Comparator<Transaccion> {

        /**
         * {@inheritDoc} 
         */
        @Override
        public int compare( Transaccion tran1, Transaccion tran2 ) {
            return tran1.obtenerFecha().compareTo( tran2.obtenerFecha() );

        }//end método compare

    }//end clase ComparadorTransaccionesPorFecha

    /**
     * La clase {@code AbstractAnimalComparator} compara animales abstractos 
     * a partir de su caravana.
     */
    class AbstractAnimalComparator implements Comparator<AbstractAnimal> {

        /**
         * {@inheritDoc} 
         */
        @Override
        public int compare( AbstractAnimal anim1, AbstractAnimal anim2 ) {
            return anim1.getCaravana().compareTo( anim2.getCaravana() );

        }//end método compare

    }//end clase AbstractAnimalComparator

}//end clase RegistroMovimientosExistencias